package dsc;

import java.io.IOException;
import java.net.InetAddress;
import java.util.Vector;
import java.util.logging.Logger;
import java.util.Date;
import java.net.*;

/**
 * In deze klasse worden Vector lijsten bijgehouden met
 * ConnectedDevices en Clients objecten (hieronder beschreven).
 * Er zijn CRUD-methodes voor deze lijsten. Dat wil zeggen dat er nieuwe
 * objectenaan de lijst kunnen worden toegevoegd, objecten kunnen worden
 * opgehaald, geupdate en/of verwijderd. Ook zijn er een paar hulp functies die
 * hieronder worden beschreven.
 *
 */
class DataStorage {

    /* De fout berichten. */
    private String succes = " Operatie is gelukt.";
    private String errCLfind = "Client kon niet worden gevonden.";
    private String errDVfind = "Device kon niet worden gevonden.";
    private String errCLupdate = "Client kon niet worden geupdate.";
    private String errDVupdate = "Device kon niet worden geupdate.";
    private String errClremove = "Client kon niet worden verwijderd.";
    private String errDVremove = "Device kon niet worden verwijderd.";
    Logger logger;
    Logging logInstance = new Logging(MessageHandler.class.getName());
    static protected Vector<ConnectedDevices> devices =
            new Vector<ConnectedDevices>();
    static protected Vector<Clients> clients = new Vector<Clients>();
    static protected Vector<String> admins = new Vector<String>();
    protected Boolean root;
    String parent = "";

    protected DataStorage() {
        logInstance = new Logging(DataStorage.class.getName());
        logger = logInstance.createLogger();

        try {
            logInstance.init(logger);
        } catch (Exception e) {
        }
    }

    /**
     * Voegt de device object aan de juiste Vector toe.
     * 
     * @param d     Device die toegevoegd moet worden.
     */
    protected void insertDevice(ConnectedDevices d) {
        devices.add(d);
    }

    /**
     * Voegt de client object aan de juiste Vector toe.
     * @param d     Client die toegevoegd moet worden.
     */
    protected void insertClient(Clients c) throws Exception {
        if (checkDuplicateNames(c.getName())) {
            throw new Exception();
        } else {
            clients.add(c);
        }
    }

    /**
     * Haalt de device op met de zelfde ipadress zoals meegegeven als argument.
     *
     * @param ipadress  Ipadress van de device die gezocht wordt.
     * @param port      De port van de device die gezocht wordt.
     *
     * @return          Connected device die aan deze parameters voldoet.
     * @throws          Wanneer het device niet gevonden kan worden.
     */
    protected ConnectedDevices getDevice(InetAddress ipadress, int port)
            throws Exception {
        for (int i = 0; i < devices.size(); i++) {
            if (devices.get(i).getIpadress().equals(ipadress)
                    && devices.get(i).getPort() == port) {
                logger.info(devices.get(i).getIpadress() + succes);
                return devices.get(i);
            }
        }
        logger.warning(errDVfind);
        throw new Exception(errDVfind);
    }

    /**
     * Haalt de device op met de zelfde tag zoals meegegeven als argument.
     *
     * @param tag       De tag van de gezochte device.
     *
     * @return          Connected device die aan deze parameters voldoet.
     * @throws          Wanneer het device niet gevonden kan worden.
     */
    protected ConnectedDevices getDevice(String tag) throws Exception {
        for (int i = 0; i < devices.size(); i++) {
            if (devices.get(i).getTag().equals(tag)) {
                logger.info(devices.get(i).getIpadress() + succes);
                return devices.get(i);
            }
        }
        logger.warning(errDVfind);
        throw new Exception(errDVfind);
    }

    /**
     * Haalt de client op met de zelfde naam zoals meegegeven als argument.
     *
     * @param name       De naam van de gezochte Client.
     *
     * @return           Clients die aan deze parameters voldoet.
     * @throws           Wanneer het client niet gevonden kan worden.
     */
    protected Clients getClient(String name) throws Exception {
        for (int i = 0; i < clients.size(); i++) {
            if (clients.get(i).getName().equals(name)) {
                logger.info(clients.get(i).getIpadress() + succes);
                return clients.get(i);
            }
        }
        logger.warning(errCLfind);
        throw new Exception(errCLfind);
    }

    /**
     * Haalt de client op met de zelfde ipadress zoals meegegeven als argument.
     *
     * @param ipadress      Ipadress van de client die gezocht wordt.     .
     * @param port          Port van de client die gezocht wordt.
     *
     * @return              Clients die aan deze parameters voldoet.
     * @throws              Wanneer de client niet gevonden kan worden.
     */
    protected Clients getClient(InetAddress ipadress, int port) throws Exception {
        for (int i = 0; i < clients.size(); i++) {
            if (clients.get(i).getIpadress().equals(ipadress)
                    && clients.get(i).getPort() == port) {
                logger.info(clients.get(i).getIpadress() + succes);
                return clients.get(i);
            }
        }
        logger.warning(errCLfind);
        throw new Exception(errCLfind);
    }

    /**
     * Verwijdert een device uit de datastorage
     *
     * @param d         De device die verwijderd moet worden.
     *
     * @return         
     */
    protected void deleteDevice(ConnectedDevices d) {
        for (int i = 0; i < devices.size(); i++) {
            if (devices.get(i).equals(d)) {
                devices.remove(i);
            }
        }
    }

    /**
     * Verwijdert een client uit de datastorage
     *
     * @param d         De device die verwijderd moet worden.
     *
     * @return
     */
    protected void deleteClient(Clients c) {
        for (int i = 0; i < clients.size(); i++) {
            if (clients.get(i).equals(c)) {
                clients.remove(i);
            }
        }
    }

    /**
     * Haalt de tijd op wanneer er voor het laatst naar deze device is
     * gepingd.
     *
     * @param d         De device waarvan de ping tijd wordt opgevraagd.
     *
     * @return          Long geeft de tijd terug in secondes.
     * @exception       Wanneer de device niet gevonden kan worden.
     */
    protected long getPingTime(ConnectedDevices d) throws Exception {
        for (int i = 0; i < devices.size(); i++) {
            if (devices.get(i).equals(d)) {
                logger.info(devices.get(i).getIpadress() + succes);
                return devices.get(i).getPing();
            }
        }
        logger.warning(errDVfind);
        throw new Exception(errDVfind);
    }

    /**
     * Update de tijd wanneer er voor het laatst is gepingd.
     *
     * @param d         De device waarvan de ping tijd wordt upgedate.
     *
     * @return
     */
    protected void setPingTime(ConnectedDevices d) {
        for (int i = 0; i < devices.size(); i++) {
            if (devices.get(i).equals(d)) {
                devices.get(i).setPing(System.currentTimeMillis() / 1000);
            }
        }
    }

    /**
     * Geeft de ping count terug van een deze device.
     *
     * @param d         De device waarvan de ping count wordt opgevraagd.
     *
     * @return          int de ping count van deze device.
     * @exception       Wanneer de device niet gevonden kan worden.
     */
    protected int getPingCount(ConnectedDevices d) throws Exception {
        for (int i = 0; i < devices.size(); i++) {
            if (devices.get(i).equals(d)) {
                logger.info(devices.get(i).getIpadress() + succes);
                return devices.get(i).getPingcount();
            }
        }
        logger.warning(errDVfind);
        throw new Exception(errDVfind);
    }

    /**
     * Hoogt de ping count van deze device met 1 op.
     *
     * @param d         De device waarvan de ping count wordt opgehoogd.
     *
     * @return 
     */
    protected void setPingCount(ConnectedDevices d) {
        for (int i = 0; i < devices.size(); i++) {
            if (devices.get(i).equals(d)) {
                devices.get(i).setPingcount(devices.get(i).getPingcount() + 1);
            }
        }
    }

    /**
     * Haalt de tijd op wanneer er voor het laatst een pong is ontvangen
     * van deze device.
     *
     * @param d         De device waarvan de pong tijd wordt opgevraagd.
     *
     * @return          Long geeft de tijd terug in secondes.
     * @exception       Wanneer de device niet gevonden kan worden.
     */
    protected long getPongTime(ConnectedDevices d) throws Exception {
        for (int i = 0; i < devices.size(); i++) {
            if (devices.get(i).equals(d)) {
                logger.info(devices.get(i).getIpadress() + succes);
                return devices.get(i).getPong();
            }
        }
        logger.warning(errDVfind);
        throw new Exception(errDVfind);
    }

    /**
     * Update de pong tijd van deze device
     *
     * @param d         De device waarvan de ping tijd wordt upgedate.
     *
     * @return
     */
    protected void setPongTime(ConnectedDevices d) throws Exception {
        for (int i = 0; i < devices.size(); i++) {
            if (devices.get(i).equals(d)) {
                devices.get(i).setPong(System.currentTimeMillis() / 1000);
            }
        }
    }

    /**
     * Haalt de lijst op van alle devices die bekend zijn bij de server.
     *
     * @return          Een lijst met alle devices.
     */
    protected Vector getDevices() {
        return devices;
    }

    /**
     * Haalt de lijst op van alle devices en actief zijn.
     *
     * @return          Een lijst met alle devices die actief zijn.
     */
    protected Vector getActiveDevices() {
        ConnectedDevices device;
        Vector<ConnectedDevices> list = new Vector<ConnectedDevices>();

        for (int i = 0; i < devices.size(); i++) {
            device = devices.get(i);
            if (device.getStatus()) {
                list.add(device);
            }
        }
        return list;
    }

    /**
     * Haalt de lijst op van alle clients die bekend zijn bij de server.
     *
     * @return          Een lijst met alle clients.
     */
    protected Vector getClients() {
        return clients;
    }

    protected Vector getAdmins() {
        Vector<Clients> list = new Vector<Clients>();
        for (int i = 0; i < clients.size(); i++) {
            if (clients.get(i).getAdmin()) {
                list.add(clients.get(i));
            }
        }
        return list;
    }

    /**
     * Maakt de lijst van devices leeg.
     *
     * @return
     */
    protected void flushDevices() {
        devices.clear();
        logger.info("cleared the devices array");
    }

    /**
     * Maakt de lijst van clients leeg.
     *
     * @return
     */
    protected void flushClients() {
        clients.clear();
        logger.info("cleared the Client array");
    }

    /**
     * Controleert op dubbele namen.
     *
     * @param name      de naam van de client.
     *
     * @return          true als naam al bestaat.
     */
    protected boolean checkDuplicateNames(String name) {
        int duplicate = 0;

        for (int i = 0; i < clients.size(); i++) {
            if (clients.get(i).getName().equals(name)) {
                duplicate++;
            }
        }
        return (duplicate > 0);
    }

    /**
     * Geeft een lijst terug van clients die te bereiken zijn via deze server.
     *
     * @param address   Het ipadress van de server.
     * @param port      De port van de server
     *
     * @return          De lijst van clients die te bereiken zijn via deze
     *                  server
     * @exception       Als de device niet bestaat.
     */
    protected Vector getClientsConnectedToServer(InetAddress address, int port)
            throws Exception {
        Clients client;
        Vector<Clients> list = new Vector<Clients>();
        ConnectedDevices device = getDevice(address, port);

        for (int i = 0; i < clients.size(); i++) {
            client = clients.get(i);
            if (device.getIpadress().equals(client.getIpadress())) {
                list.add(clients.get(i));
            }
        }
        return list;
    }

    /**
     * Geeft een lijst terug van clients die te bereiken zijn via deze server.
     *
     * @param tag       De tag van de server.
     *
     * @return          De lijst van clients die te bereiken zijn via deze
     *                  server
     * @exception       Als de device niet bestaat.
     */
    protected Vector getClientsConnectedToServer(String tag) throws Exception {
        Clients client;
        Vector<Clients> list = new Vector<Clients>();

        ConnectedDevices device = getDevice(tag);
        for (int i = 0; i < clients.size(); i++) {
            client = clients.get(i);
            if (device.getIpadress().equals(client.getIpadress())) {
                list.add(clients.get(i));
            }
        }
        return list;
    }

    /**
     * Geeft een lijst terug van clients die te bereiken zijn via deze server.
     *
     * @param d         De device object van deze server.
     *0
     * @return          De lijst van clients die te bereiken zijn via deze
     *                  server
     * @exception       Als de device niet bestaat.
     */
    protected Vector getClientsConnectedToServer(ConnectedDevices d)
            throws Exception {
        Clients client;
        Vector<Clients> list = new Vector<Clients>();
        ConnectedDevices device = d;

        for (int i = 0; i < clients.size(); i++) {
            client = clients.get(i);
            if (device.getIpadress().equals(client.getIpadress())
                    && (device.getPort() == client.getPort())) {
                list.add(clients.get(i));
            }
        }
        return list;
    }

    /**
     * Geeft een lijst terug van clients die NIET te bereiken zijn via deze
     * server. Geeft ook geen admins terug.
     *
     * @param d         De device object van deze server.
     *
     * @return          De lijst van clients die NIET te bereiken zijn via deze
     *                  server
     * @exception       Als de device niet bestaat.
     */
    protected Vector getClientsNotConnectedToServer(ConnectedDevices d)
            throws Exception {
        Clients client;
        Vector<Clients> list = new Vector<Clients>();
        ConnectedDevices device = d;

        for (int i = 0; i < clients.size(); i++) {
            client = clients.get(i);
            if (!(device.getIpadress().equals(client.getIpadress())
                    && (device.getPort() == client.getPort()))
                    && !client.getAdmin()) {
                list.add(clients.get(i));
            }
        }
        return list;
    }

    /**
     * Geeft een lijst terug van clients die GEEN admins zijn
     *
     * @return          De lijst van clients die geen admins zijn.
     * @exception       Als de device niet bestaat.
     */
    protected Vector getNonAdminClients() throws Exception {
        Clients client;
        Vector<Clients> list = new Vector<Clients>();

        for (int i = 0; i < clients.size(); i++) {
            client = clients.get(i);
            if (!client.getAdmin() && client.getStatus()) {
                list.add(client);
            }
        }
        return list;
    }

    /**
     * Geeft een lijst terug van clients die GEEN admins zijn
     *
     * @return          De lijst van clients die geen admins zijn.
     * @exception       Als de device niet bestaat.
     */
    protected Vector getActiveClients() throws Exception {
        Clients client;
        Vector<Clients> list = new Vector<Clients>();
        for (int i = 0; i < clients.size(); i++) {
            client = clients.get(i);
            if (client.getStatus()) {
                list.add(client);
            }
        }
        return list;
    }

    /**
     * Geeft een lijst terug van clients die GEEN admins zijn
     *
     * @return          De lijst van clients die geen admins zijn.
     * @exception       Als de device niet bestaat.
     */
    protected void activateClients() throws Exception {

        for (int i = 0; i < clients.size(); i++) {
            clients.get(i).setStatus(true);
        }
    }

    /**
     * Verwijderd alle clients die te bereiken zijn via deze server.
     * server.
     *
     * @param d         De device object van deze server.
     *
     * @return         
     * @exception       Als de device niet bestaat.
     */
    protected void removeClientsConnectedToServer(ConnectedDevices d) {
        for (int i = 0; i < clients.size(); i++) {
            if (clients.get(i).getIpadress().equals(d.getIpadress())
                    && (clients.get(i).getPort() == d.getPort())) {
                clients.remove(i);
            }
        }
    }

    /**
     * Geeft de status van alle data van deze server.
     *
     * @return              Print alle waardes naar de log.
     */
    protected void getStatus() {
        String temp = "", temp2 = "";
        String parent = "", client = "", device = "";
        if (!parent.isEmpty()) {
            parent = "Dit is de Parent : " + parent + "\n";
        } else {
            parent = "Dit is de Parent :  none\n";
        }

        if (devices.isEmpty()) {
            device = "Dit zijn de devices : none\n";
        } else {
            for (int i = 0; i < devices.size(); i++) {
                if (!devices.get(i).getTag().isEmpty()) {
                    temp2 += devices.get(i).getTag() + " ";
                }
            }
            device = "Dit zijn de devices : " + temp2 + "\n";
        }

        if (client.isEmpty()) {
            client = "Dit zijn de clients : none\n";
        } else {
            for (int i = 0; i < clients.size(); i++) {
                temp2 += clients.get(i).getName() + " ";
            }
            client += "Dit zijn de clients : " + temp + "\n";
        }
        logger.info(parent + device + clients);
    }

    /**
     * Set de naam van de parent van deze server.
     *
     * @param s             De tag van de server die parent is.
     *
     * @return             
     */
    protected void setParent(String s) {
        this.parent = s;
    }
}
